package com.xmzs.web.service.impl;


import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.xmzs.common.chat.constant.OpenAIConst;
import com.xmzs.common.chat.domain.request.ChatProcessRequest;
import com.xmzs.common.chat.entity.chat.BaseChatCompletion;
import com.xmzs.common.chat.entity.chat.ChatCompletion;
import com.xmzs.common.core.domain.model.LoginUser;
import com.xmzs.common.core.exception.ServiceException;
import com.xmzs.common.core.exception.base.BaseException;
import com.xmzs.common.satoken.utils.LoginHelper;
import com.xmzs.system.domain.SysUser;
import com.xmzs.system.domain.bo.ChatMessageBo;
import com.xmzs.system.mapper.SysUserMapper;
import com.xmzs.system.service.IChatMessageService;
import com.xmzs.web.service.ChatService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter;

import java.math.BigDecimal;

/**
 * @author hncboy
 * @date 2023/3/22 19:41
 * 聊天相关业务实现类
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ChatServiceImpl implements ChatService {

    private final SysUserMapper sysUserMapper;


    private final IChatMessageService chatMessageService;

    @Override
    public ResponseBodyEmitter chatProcess(ChatProcessRequest chatProcessRequest) {
        return new ResponseBodyEmitter(0L);
    }

    /**
     * 根据消耗的tokens扣除余额
     *
     * @param modelName
     * @param tokens    消耗的tokens
     * @param type
     */
    @Override
    public void deductToken(String modelName, int tokens, String type, String countText,BigDecimal number) {
        // 计算tokens消耗的余额
        if (modelName.equals(ChatCompletion.Model.GPT_3_5_TURBO_0613.getName())) {
            // GPT_3.5  输入：0.007元/1000tokens 输出：0.014元/1000tokens
            number = calculateCostForModel(tokens, type, OpenAIConst.GPT3_IN, OpenAIConst.GPT3_OUT);
        } else if (modelName.equals(ChatCompletion.Model.GPT_4_1106_PREVIEW.getName())
            || modelName.equals(ChatCompletion.Model.GPT_4_VISION_PREVIEW.getName())
        ) {
            // GPT_4  输入：0.07元/1000tokens 输出：0.21元/1000tokens
            number = calculateCostForModel(tokens, type, OpenAIConst.GPT4_IN, OpenAIConst.GPT4_OUT);
        }
        // 保存消息记录
        ChatMessageBo chatMessageBo = new ChatMessageBo();
        chatMessageBo.setModelName(modelName);
        chatMessageBo.setContent(countText);
        chatMessageBo.setTotalTokens(tokens);
        chatMessageBo.setDeductCost(number);
        chatMessageBo.setUserId(LoginHelper.getLoginUser().getUserId());
        chatMessageService.insertByBo(chatMessageBo);
        deductBalance(number);
    }

    /**
     * 扣除用户余额
     */
    public void deductBalance(BigDecimal number) {

        LoginUser loginUser = LoginHelper.getLoginUser();
        if (loginUser == null) {
            throw new BaseException("用户未登录！");
        }
        SysUser sysUser = sysUserMapper.selectById(loginUser.getUserId());
        if (sysUser != null) {
            // 获取账户余额
            BigDecimal currentBalance = sysUser.getUserBalance();
            // 计算需要扣除的费用
            BigDecimal updatedBalance = currentBalance.subtract(number);
            if (updatedBalance.signum() > 0) {
                sysUserMapper.update(null,
                    new LambdaUpdateWrapper<SysUser>()
                        .set(SysUser::getUserBalance, updatedBalance)
                        .eq(SysUser::getUserId, loginUser.getUserId()));
            } else {
                // 如果余额不足直接清空余额
                sysUserMapper.update(null,
                    new LambdaUpdateWrapper<SysUser>()
                        .set(SysUser::getUserBalance, BigDecimal.ZERO)
                        .eq(SysUser::getUserId, loginUser.getUserId()));
                throw new ServiceException("余额不足，请联系管理员充值！");
            }
        }
    }

    /**
     * 计算费用
     *
     * @param tokens  消耗的tokens
     * @param type    对话类型
     * @param inRate  输入费率
     * @param outRate 输出费率
     */
    private BigDecimal calculateCostForModel(int tokens, String type, double inRate, double outRate) {
        BigDecimal ba = new BigDecimal(tokens / 1000.0);
        BigDecimal bb = BigDecimal.valueOf(BaseChatCompletion.ChatType.CHAT_IN.name().equals(type) ? inRate : BaseChatCompletion.ChatType.CHAT_OUT.name().equals(type) ? outRate : 0.0);
        return ba.multiply(bb);
    }

}
